es6-08 新提案

ES2020

  1. 可选链:?

    之前:obj.a.b 当找不到a再找b,报错

    现在:obj.a?.b 找不到a就不找b

    obj?.prop
    obj?.[expr]
    arr?.[index]
    func?.(args)
    
  2. 双问号:??,false、0、""都为真;

    0 ?? 1 // 0
    
  3. 动态import

    const loadHeader = () => {
        import('./header').then(module => {})
    }
    
  4. promise.allSettled

    解决promise.all其中reject时就返回了,allSettled则会请求完所有的接口,最终返回resolve,里面为所有结果,包括成功和失败;

  5. globalThis:解决node环境没有window,需要用global,现在会根据环境判断使用window或global;

  6. BigInt:整数类型

  7. String.matchall:正则匹配将所有的字符串都返回回来,为一个数组

可选链

配置babel解析:https://www.jianshu.com/p/bca4ce835caf

es6教程:https://es6.ruanyifeng.com/#docs/object#%E9%93%BE%E5%88%A4%E6%96%AD%E8%BF%90%E7%AE%97%E7%AC%A6

do表达式

由于块级作用域只是一个语句,没有返回值,do表达式增加了返回值

let x = do {
    let t = f();
    t * t + 1;
} // 变量中的x为最终 t * t +1的值

do的表达式的好处是可以封装多个语句

let x = do {
  if (foo()) { f() }
  else if (bar()) { g() }
  else { h() }
};
// 哪个函数执行,就会将哪个函数的结果赋值给x

do可以用在jsx中,比三元运算符更好用

return (
	<nav>
    	{
            do {
                if(true) {
                    <Logout />
                } else {
                    <LoginButton />
                }
            }
        }
    </nav>
)

throw 表达式

js规定throw只是一个命令,用来抛出错误,不能用于表达式中

console.log(throw new Error()) // 报错

console.log的参数必须是表达式,否则就报错

新的提案支持throw用于表达式了

function save(name = throw new TypeError('Argument required')) {};
function set(value) {
    let a = value || throw new Error('Invalid value')
}

函数的部分执行

以前使用bind来生成新的函数

function add(x, y) {return x + y}
var add7 = add.bind(null, 7)
add7(3) // 10

现在

var add7 = add(?, 7)
add7(3) // 10

新提案中,?表示单个参数的占位符,...是多个参数的占位符

const f = (x, ...y) => [x, ...y]
const g = f(x, ?);
g(2,3,4) // 

// 当g的值为不同的参数调用时
f(x, ?)   // [x, 2]
f(x, ...)  // [x, 2,3,4]
f(?, x)  // [2, x]
f(..., x)  // [2,3,4,x]
f(?, x, ?)  // [2, x, 3]
f(..., x, ...) // [2,3,4,x,2,3,4]

注意点

  1. 原函数发生变化,执行的结果也会不同

    let f = (x, y) => x + y;
    
    const g = f(?, 3);
    g(1); // 4
    
    // 替换函数 f
    f = (x, y) => x * y;
    
    g(1); // 3
    
  2. 如果传入的值是表达式,每次调用时会重新求值

    let a = 3;
    const f = (x, y) => x + y;
    
    const g = f(?, a);
    g(1); // 4
    
    // 改变 a 的值
    a = 10;
    g(1); // 11
    

管道运算符

管道是一个运算符,写作|>,左边是一个表达式,右边是一个函数,管道最大的好处是可以嵌套函数,写从左到右的链式表达式

function doubleSay (str) {
  return str + ", " + str;
}

function capitalize (str) {
  return str[0].toUpperCase() + str.substring(1);
}

function exclaim (str) {
  return str + '!';
}

// 以前的写法
exclaim(capitalize(doubleSay('hello')))
// "Hello, hello!"

// 管道的写法
'hello'
	|> doubleSay
	|> capitalize
	|> exclaim
// 'Hello, hello!'

管道运算符只能传递一个值,如果多个参数可以进行柯里化

function double (x) { return x + x; }
function add (x, y) { return x + y; }

let person = { score: 25 };
person.score
  |> double
  |> (_ => add(7, _))
// 57

管道运算符也可以用于await函数

x |> await f
// 等同于
await f(x)

const userAge = userId |> await fetchUserById |> getAgeFromUser;
// 等同于
const userAge = getAgeFromUser(await fetchUserById(userId));

Math.signbit()

用来判断一个值的正负,可以正确判断0是否是-0;

Math.signbit(0) // false
Math.signbit(-0) // true

双冒号运算符

函数绑定运算符::,双冒号左边是一个对象,右边是一个函数;

foo::bar
// 等同于
bar.bind(foo);

foo::bar(...arguments);
// 等同于
bar.apply(foo, arguments);

如果左边为空,右边是一个对象的方法,则绑定在该对象上面

var method = obj::obj.foo;
// 等同于
var method = ::obj.foo;

let log = ::console.log;
// 等同于
var log = console.log.bind(console);

如果运算符的结果还是一个对象,可以采用链式写法

import { map, takeWhile, forEach } from "iterlib";

getPlayers()
::map(x => x.character())
::takeWhile(x => x.strength > 100)
::forEach(x => console.log(x));

Realm

创建一个window对象的副本,可以前往官网查看https://es6.ruanyifeng.com/#docs/proposals#Realm-API

#!命令

支持在脚本之前添加一行命令

#!/usr/bin/env node

之后执行时

# 以前执行脚本的方式
$ node hello.js

# hashbang 的方式
$ ./hello.js

import.meta

返回引入模块的元信息

  • import.meta.url 返回当前的url路径

  • import.meta.scriptElement 返回加载script的元素

    // HTML 代码为
    // <script type="module" src="my-module.js" data-foo="abc"></script>
    
    // my-module.js 内部执行下面的代码
    import.meta.scriptElement.dataset.foo
    // "abc"